﻿//#include <stdio.h>
//2023-12-4 13:29:39 Monday
//int main()
//{
//	int a = 10;
//	int b = 20;
//	int c = 0;
//	printf("%d %d\n", a, b);
//
//	c = a;
//	a = b;
//	b = c;
//
//	printf("%d %d\n", a, b);
//
//	return 0;
//}
//void Swap(int x, int y)
//{
//	int z = x;
//	x = y;
//	y = z;
//}
//
//int main()
//{
//	int a = 10;
//	int b = 20;
//	printf("%d %d\n", a, b);
//
//	Swap(a, b);
//	printf("%d %d\n", a, b);
//
//	return 0;
//}

//2023-12-6 13:32:42 Tuesday.
//int main()
//{
//	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//
//	printf("%p\n", &arr[0]);
//	printf("%p\n", arr);
//	printf("%p\n", &arr);
//
//	return 0;
//}

//int main()
//{
//	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//
//	printf("&arr[0]     = %p\n", &arr[0]);
//	printf("&arr[0] + 1 = %p\n", &arr[0] + 1);
//	printf("arr         = %p\n", arr);
//	printf("arr + 1     = %p\n", arr + 1);
//	printf("&arr        = %p\n", &arr);
//	printf("&arr + 1    = %p\n", &arr + 1);
//
//	return 0;
//}































//2023-12-31 Sunday 18:06:08
//6.2 如何规避野指针
//6.2.1 指针初始化
//#include <stdio.h>
//
//int main()
//{
//	int a = 10;
//	int* p = &a;//初始化
//
//	int* ptr = NULL;//空指针 使用NULL需引头文件#include <stdio.h>
//	*ptr = 100;//error，空指针的英文：nullptr 
//	printf("%d\n", *ptr);//因为NULL使用不了
//
//	return 0;
//}


//使用指针之前检查指针的有效性
//#include <stdio.h>
//
//int mian()
//{
//	int n = 10;
//	int* p = &n;
//
//	int* prt = NULL;
//
//	if (p != NULL)
//	{
//		//使用p
//	}
//
//	if (ptr != NULL)
//	{
//		*ptr = 100;
//	}
//
//	return 0;
//}


//6.2.2 ⼩⼼指针越界
//arr[0] - 数组首元素的地址
//arr - 数组名也是首元素的地址
//使用指针打印数组
//#include <stdio.h>
//
//int main()
//{
//	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = arr;
//	int i = 0;
//
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *p);
//		p++;
//	}
//
//	return 0;
//}
//因为最终的p已经加到11了，所以如果你再继续使用就会造成越界访问，p也就是野指针了


//6.2.3 指针变量不再使⽤时，及时置NULL，指针使⽤之前检查有效性
//p = NULL;//联系上述代码，不再使用p了，就直接置NULL
//如果想再继续使用，就
//p = arr;//赋值首元素地址或者其他地址，只要不越界就行

//if (p != NULL)判断再去使用
//{
//	...
//}


//6.2.4 避免返回局部变量的地址
//返回栈空间的地址的问题，很容易造成野指针问题
//int* test()
//{
//	//局部变量
//	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//
//	return &arr;
//}
//
//
//int main()
//{
//	int* p = test();
//	//p就是野指针
//
//	return 0;
//}


//7. assert断⾔
//#include <assert.h>
//
//int main()
//{
//	int* p = NULL;
//
//	if (p != NULL)//为真
//	{
//
//	}
//	else//为假
//	{
//
//	}
//	//这种方式太麻烦了，效率低
//
//	assert(p != NULL);
//
//	return 0;
//}


//8. 指针的使⽤和传址调⽤
//8.1 传址调⽤
//交换两个整型变量的值
//#include <stdio.h>
//
//int main()
//{
//	int a = 10;
//	int b = 20;
//	int count = 0;
//
//	printf("交换前：%d %d\n", a, b);
//
//	count = a;
//	a = b;
//	b = count;
//
//	printf("交换后：%d %d\n", a, b);
//
//	return 0;
//}


//写⼀个函数，交换两个整型变量的值
//#include <stdio.h>
//
//void Swap(int x, int y)
//{
//	int count = x;
//	x = y;
//	y = count;//临时变量
//}
//
//int main()
//{
//	int a = 10;
//	int b = 20;
//
//	printf("交换前：%d %d\n", a, b);
//
//	Swap(a, b);
//
//	printf("交换后：%d %d\n", a, b);
//
//	return 0;
//}
//传值调用的时候，函数实参传递给形参后，形参是实参的一份临时拷贝，形参有自己独立的空间
//所以对形参的修改不能影响实参


//传址调用
//#include <stdio.h>
//
//void Swap(int* px, int* py)
//{
//	int count = * px;//count = a;
//	*px = *py;       //a = b;
//	*py = count;     //b = count;
//}
//
//int main()
//{
//	int a = 10;
//	int b = 20;
//
//	printf("交换前：%d %d\n", a, b);
//
//	Swap(&a, &b);
//
//	printf("交换后：%d %d\n", a, b);
//
//	return 0;
//}


//int main()
//{
//	int a = 10;
//	int* p = &a;
//	*p = 20;
//
//	return 0;
//}


//传值调用
//写一个函数，求最大值
//#include <stdio.h>
//
//int Max(int x, int y)
//{
//	if (x > y)
//	{
//		return x;
//	}
//	else
//	{
//		return y;
//	}
//}
//
//int main()
//{
//	int a = 10;
//	int b = 20;
//
//	int ret = Max(a, b);
//
//	printf("%d\n", ret);
//
//	return 0;
//}


//什么时候用传值调用，什么时候用传址调用呢？
//看是否要改变值，如果要看变值就必须用传址
//否则用传值


//2024-1-1 Monday 13:28:09
//8.2 strlen的模拟实现
//健壮性/鲁棒性
//define NDEBUG
//#include <assert.h>
//#include <stdio.h>
//
//size_t my_strlen(const char* str)//不希望下面str指向的对像被改，如果被改了，你看怎么办
//{
//	size_t count = 0;
//	assert(str != NULL);//在使用指针之前，检查指针的有效性
//
//	//while(*str != '\0')
//	while(*str++ != '\0')//因为是后置加加，所以让*str先判断,再让str++，这种代码可读性差，我还是不写这种，理解就行，这里涉及到优先级问题，有点麻烦了，其实我还是不理解的，我只是靠结果去推罢了
//	{
//		count++;
//		//str++;//没必要解引用了，已经限制你不能够被改了，让地址走就行，本身地址就在首位，万一被改了，计数容易有问题
//	}
//
//	return count;
//}
// 
//int main()
//{
//	char arr[] = "Hello";
//	size_t len = my_strlen(arr);
//
//	printf("len = %d\n", len);
//
//	return 0;
//}


//第12讲：深⼊理解指针(2)
//1. 数组名的理解
//2. 使⽤指针访问数组
//3. ⼀维数组传参的本质
//4. 冒泡排序
//5. ⼆级指针
//6. 指针数组
//7. 指针数组模拟⼆维数组

//1. 数组名的理解
//数组名是数组首元素的地址
//但是也有2个例外：
//1. sizeof(数组名)，这里的数组名表示整个数组，sizeof(数组名)计算的是整个数组的大小，单位是字节
//2. &数组名，这里的数组名表示整个数组，&数组名：取出的是整个数组的地址
//除此之外，遇到的所有的数组名都是数组首元素的地址
//#include <stdio.h>
//
//int main()
//{
//	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//
//	printf("&arr[0]     = %p\n", &arr[0]);
//	printf("&arr[0] + 1 = %p\n", &arr[0] + 1);
//
//	printf("arr         = %p\n", arr);
//	printf("arr     + 1 = %p\n", arr + 1);
//
//	printf("&arr        = %p\n", &arr);
//	printf("&arr    + 1 = %p\n", &arr + 1);
//
//	printf("sizeof(arr) = %d\n", sizeof(arr));//40
//
//	return 0;
//}


//第12讲：深⼊理解指针(2)
//1. 数组名的理解
//2. 使⽤指针访问数组
//3. ⼀维数组传参的本质
//4. 冒泡排序
//5. ⼆级指针
//6. 指针数组
//7. 指针数组模拟⼆维数组

//2. 使⽤指针访问数组
//输入元素再输出元素
//#pragma warning(disable : 4996)
//
//#include <stdio.h>
//
//int main()
//{
//	int arr[10] = { 0 };
//	/*[] [] [] [] [] [] [] [] [] []
//	0  1  2  3  4  5  6  7  8  9*/
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = arr;
//	int i = 0;
//
//	for (i = 0; i < sz; i++)
//	{
//		scanf("%d", p + i);//输入的是地址，因为p+i就是地址，所以不用取地址
//	}
//
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//
//	return 0;
//}


//#pragma warning(disable : 4996)
//
//#include <stdio.h>
//
//int main()
//{
//	int arr[10] = { 0 };
//	/*[] [] [] [] [] [] [] [] [] []
//	0  1  2  3  4  5  6  7  8  9*/
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = arr;
//	int i = 0;
//
//	for (i = 0; i < sz; i++)
//	{
//		scanf("%d", p++);//先使用后加加，也就是先输入p再加往后走 // 上面代码是让i变，这个是让p变
//	}
//
//	p = arr;//不过p变完后会变成10，为了避免越界，先给回首地址，下面再去使用
//
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//
//	return 0;
//}


//多种写法
//#include <stdio.h>
//
//int main()
//{
//	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = arr;
//	int i = 0;
//
//	for (i = 0; i < sz; i++)
//	{
//		//printf("%d ", *(p + i));
//		//printf("%d ", *(arr + i));
//		//printf("%d ", arr[i]);
//		//printf("%d ", p[i]);
//     	//arr[i] == *(arr+i)//arr[i]只是形式上，本质上编译的时候会转成指针的方式运算，他们是等价的
//		//*(p+i) == p[i]
//		//arr[i] == *(arr + i) == *(i + arr) == i[arr]
//		//printf("%d\n", i[arr]);//3 + 5//5 + 3支持交换率
//		//不推荐这种写法，你爱写什么写什么要易读性强就好
//	}
//
//	return 0;
//}


//#include <stdio.h>
//
//int main()
//{
//	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int* p = arr;
//	int i = 0;
//
//	for (i = 0; i < sz; i++)
//	{
//		printf("%p ==== %p\n", p + i, &arr[i]);//你看的确是相等的
//	}
//
//	return 0;
//}




























//2024-1-3 Tuesday 13:32:46
//第12讲：深⼊理解指针(2)
//⽬录
//1. 数组名的理解
//2. 使⽤指针访问数组
//3. ⼀维数组传参的本质
//4. 冒泡排序
//5. ⼆级指针
//6. 指针数组
//7. 指针数组模拟⼆维数组

//1. 数组名的理解
//int main()
//{
//	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int* p = &arr[0];
//
//	return 0;
//}


//#include <stdio.h>
//
//int main()
//{
//	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//
//	printf("&arr[0] = %p\n", &arr[0]);
//	printf("arr     = %p\n", arr);
//
//	return 0;
//}


//#include <stdio.h>
//
//int main()
//{
//	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//
//	printf("%d\n", sizeof(arr));
//
//	return 0;
//}


//2024-1-4 Wednesday 13:30:37
//#include <stdio.h>
//
//int main()
//{
//	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 
//	printf("&arr[0] = %p\n", &arr[0]);
//	printf("arr     = %p\n", arr);
//	printf("&arr    = %p\n", &arr);
//
//	return 0;
//}


//#include <stdio.h>
//
//int main()
//{
//	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//
//	printf("&arr[0]     = %p\n", &arr[0]);
//	printf("&arr[0] + 1 = %p\n", &arr[0] + 1);
//	
//	printf("arr         = %p\n", arr);
//	printf("arr + 1     = %p\n", arr + 1);
//
//	printf("&arr        = %p\n", &arr);
//	printf("&arr + 1    = %p\n", &arr + 1);
//
//	return 0;
//}


//第12讲：深⼊理解指针(2)
//⽬录
//1. 数组名的理解
//2. 使⽤指针访问数组
//3. ⼀维数组传参的本质
//4. 冒泡排序
//5. ⼆级指针
//6. 指针数组
//7. 指针数组模拟⼆维数组

//2024-1-5 Friday 13:42:29
//2. 使⽤指针访问数组
//输入再输出数组
//#pragma warning(disable : 4996)
//#include <stdio.h>
//
//int main()
//{
//	int arr[10] = { 0 };
//	int* p = arr;
//	int i = 0;
//
//	for (i = 0; i < 10; i++)
//	{
//		//scanf("%d", p + i);
//		scanf("%d", p++);;//让p变
//		          //也可以这样写arr++;
//	}
//
//	p = arr;//置为0，避免数组越界访问，导致野指针
//
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", *(p + i));//也可以写成p[i]
//	}
//
//	return 0;
//}


//第12讲：深⼊理解指针(2)
//⽬录
//1. 数组名的理解
//2. 使⽤指针访问数组
//3. ⼀维数组传参的本质
//4. 冒泡排序
//5. ⼆级指针
//6. 指针数组
//7. 指针数组模拟⼆维数组

//3. ⼀维数组传参的本质
//在函数外部和内部计算元素个素
//#include <stdio.h>
//
//void test(int arr[])
//{
//	int sz2 = sizeof(arr) / sizeof(arr[0]);
//
//	printf("sz2 = %d\n", sz2);
//}
//
//int main()
//{
//	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int sz1 = sizeof(arr) / sizeof(arr[0]);
//	printf("sz1 = %d\n", sz1);
//	test(arr);
//	
//	return 0;
//}


//在函数内部和外部用sizeof计算arr的大小
//#include <stdio.h>
//
//void test1(int arr[])//参数写成数组形式，本质上还是指针
//{
//	printf("%d\n", sizeof(arr));
//}
//
//void test2(int* arr)//参数写成指针形式
//{
//	printf("%d\n", sizeof(arr));//计算一个指针变量的大小4 or 8
//}
//
//int main()
//{
//	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//
//	test1(arr);
//
//	return 0;
//}


//2024-1-6 Saturday 13:28:22
//第12讲：深⼊理解指针(2)
//⽬录
//1. 数组名的理解
//2. 使⽤指针访问数组
//3. ⼀维数组传参的本质
//4. 冒泡排序
//5. ⼆级指针
//6. 指针数组
//7. 指针数组模拟⼆维数组

//4. 冒泡排序
//方法1
//#include <stdio.h>
//
//void bubble_sort(int arr[], int sz)//参数接受数组元素个数
//{
//	int i = 0;
//	for (i = 0; i < sz - 1; i++)
//	{
//		int j = 0;
//		for (j = 0; j < sz - i - 1; j++)
//		{
//			if (arr[j] > arr[j + 1])
//			{
//				int temp = 0;
//				temp = arr[j];
//				arr[j] = arr[j + 1];
//				arr[j + 1] = temp;
//			}
//		}
//	}
//}
//
//void print(int arr[], int sz)
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//
//	printf("\n");
//}
//
//int main()
//{
//	int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//
//	bubble_sort(arr, sz);
//	print(arr, sz);
//
//	return 0;
//}


//方法2 - 优化
#include <stdio.h>

void bubble_sort(int* arr, int sz)//参数接受数组元素个数
{
	int i = 0;

	for (i = 0; i < sz - 1; i++)//决定趟数
	{
		int flag = 1;//假设这一趟已经有序了
		int j = 0;

		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])//决定对数
			{
				flag = 0;//发生交换就说明，无序
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}

		if (1 == flag)//这一趟没交换就说明已经有序了，后续无序排序了
			break;
	}
}

void print(int* arr, int sz)
{
	int i = 0;

	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}

	printf("\n");
}

int main()
{
	int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	bubble_sort(arr, sz);
	print(arr, sz);

	return 0;
}

